Tanuld meg, hogyan építhetsz robusztus és skálázható socket szervereket a Python SocketServer moduljával. Fedezd fel az alapvető koncepciókat, gyakorlati példákat és fejlett technikákat több kliens kezelésére.
Socket Server Frameworkök: Gyakorlati útmutató a Python SocketServer moduljához
A mai összekapcsolt világban a socket programozás létfontosságú szerepet játszik a különböző alkalmazások és rendszerek közötti kommunikáció lehetővé tételében. A Python SocketServer
modulja egyszerűsített és strukturált módot kínál hálózati szerverek létrehozására, elvonatkoztatva a mögöttes bonyolultság nagy részét. Ez az útmutató végigvezeti a socket szerver frameworkök alapvető koncepcióin, a Python SocketServer
moduljának gyakorlati alkalmazásaira összpontosítva. Különféle szempontokat fogunk érinteni, beleértve az alapvető szerver beállítást, több kliens egyidejű kezelését és a megfelelő szervertípus kiválasztását az egyedi igényeidhez. Akár egy egyszerű csevegő alkalmazást, akár egy komplex elosztott rendszert építesz, a SocketServer
megértése kulcsfontosságú lépés a hálózati programozás elsajátításában Pythonban.
A Socket Szerverek Megértése
A socket szerver egy olyan program, amely egy adott porton figyel a bejövő klienskapcsolatokra. Amikor egy kliens csatlakozik, a szerver elfogadja a kapcsolatot, és létrehoz egy új socketet a kommunikációhoz. Ez lehetővé teszi a szerver számára, hogy egyszerre több klienst kezeljen. A Python SocketServer
modulja egy keretrendszert biztosít az ilyen szerverek felépítéséhez, kezelve a socketkezelés és a kapcsolatkezelés alacsony szintű részleteit.
Alapvető Koncepciók
- Socket: A socket egy kétirányú kommunikációs kapcsolat végpontja két, a hálózaton futó program között. Analóg a telefon aljzathoz – az egyik program egy socketbe csatlakozik, hogy információt küldjön, a másik pedig egy másik socketbe csatlakozik, hogy fogadja azt.
- Port: A port egy virtuális pont, ahol a hálózati kapcsolatok kezdődnek és végződnek. Ez egy numerikus azonosító, amely megkülönbözteti az egy gépen futó különböző alkalmazásokat vagy szolgáltatásokat. Például a HTTP általában a 80-as portot használja, a HTTPS pedig a 443-as portot.
- IP Cím: Az IP (Internet Protocol) cím egy numerikus címke, amelyet minden olyan eszközhöz hozzárendelnek, amely egy számítógép-hálózathoz csatlakozik, és amely az Internet Protokollt használja a kommunikációhoz. Azonosítja az eszközt a hálózaton, lehetővé téve más eszközök számára, hogy adatokat küldjenek neki. Az IP címek olyanok, mint a postai címek a számítógépek számára az interneten.
- TCP vs. UDP: A TCP (Transmission Control Protocol) és az UDP (User Datagram Protocol) két alapvető szállítási protokoll, amelyet a hálózati kommunikációban használnak. A TCP kapcsolat-orientált, megbízható, rendezett és hibát ellenőrzött adatátvitelt biztosít. Az UDP kapcsolat nélküli, gyorsabb, de kevésbé megbízható kézbesítést kínál. A TCP és az UDP közötti választás az alkalmazás követelményeitől függ.
A Python SocketServer Modul Bemutatása
A SocketServer
modul leegyszerűsíti a hálózati szerverek létrehozásának folyamatát Pythonban azáltal, hogy magas szintű interfészt biztosít a mögöttes socket API-hoz. Elvonatkoztatja a socketkezelés bonyolultságának nagy részét, lehetővé téve a fejlesztők számára, hogy az alkalmazáslogikára összpontosítsanak, nem pedig az alacsony szintű részletekre. A modul számos osztályt biztosít, amelyek felhasználhatók különböző típusú szerverek létrehozására, beleértve a TCP szervereket (TCPServer
) és az UDP szervereket (UDPServer
).
Kulcsfontosságú Osztályok a SocketServerben
BaseServer
: ASocketServer
modul összes szerverosztályának alaposztálya. Meghatározza az alapvető szerver viselkedést, mint például a kapcsolatok figyelését és a kérések kezelését.TCPServer
: ABaseServer
alosztálya, amely egy TCP (Transmission Control Protocol) szervert valósít meg. A TCP megbízható, rendezett és hibát ellenőrzött adatátvitelt biztosít.UDPServer
: ABaseServer
alosztálya, amely egy UDP (User Datagram Protocol) szervert valósít meg. Az UDP kapcsolat nélküli és gyorsabb, de kevésbé megbízható adatátvitelt biztosít.BaseRequestHandler
: A kérelemkezelő osztályok alaposztálya. A kérelemkezelő felelős az egyes klienskérések kezeléséért.StreamRequestHandler
: ABaseRequestHandler
alosztálya, amely a TCP kéréseket kezeli. Kényelmes metódusokat biztosít az adatok olvasásához és írásához a kliens socketbe streamekként.DatagramRequestHandler
: ABaseRequestHandler
alosztálya, amely az UDP kéréseket kezeli. Metódusokat biztosít datagramok (adatcsomagok) fogadásához és küldéséhez.
Egyszerű TCP Szerver Létrehozása
Kezdjük egy egyszerű TCP szerver létrehozásával, amely figyeli a bejövő kapcsolatokat, és visszhangozza a kapott adatokat a kliensnek. Ez a példa bemutatja a SocketServer
alkalmazás alapvető felépítését.
Példa: Echo Szerver
Íme a kód egy alapvető echo szerverhez:
import SocketServer
class MyTCPHandler(SocketServer.BaseRequestHandler):
"""
The request handler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print "{} wrote:".format(self.client_address[0])
print self.data
# just send back the same data you received.
self.request.sendall(self.data)
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
Magyarázat:
- Importáljuk a
SocketServer
modult. - Definiálunk egy kérelemkezelő osztályt, a
MyTCPHandler
-t, amely aSocketServer.BaseRequestHandler
-ből öröklődik. - A
handle()
metódus a kérelemkezelő magja. Akkor hívódik meg, amikor egy kliens csatlakozik a szerverhez. - A
handle()
metóduson belül adatokat fogadunk a klienstől aself.request.recv(1024)
segítségével. Ebben a példában a maximálisan fogadott adatmennyiséget 1024 bájtra korlátozzuk. - Kinyomtatjuk a kliens címét és a kapott adatokat a konzolra.
- A kapott adatokat visszaküldjük a kliensnek a
self.request.sendall(self.data)
segítségével. - Az
if __name__ == "__main__":
blokkban létrehozunk egyTCPServer
példányt, és a localhost címhez és a 9999-es porthoz kötjük. - Ezután meghívjuk a
server.serve_forever()
metódust a szerver elindításához, és futtatjuk mindaddig, amíg a programot meg nem szakítjuk.
Az Echo Szerver Futtatása
Az echo szerver futtatásához mentsd el a kódot egy fájlba (pl. echo_server.py
), és futtasd a parancssorból:
python echo_server.py
A szerver elkezd figyelni a kapcsolatokra a 9999-es porton. Ezután csatlakozhatsz a szerverhez egy kliens programmal, mint például a telnet
vagy a netcat
. Például a netcat
használatával:
nc localhost 9999
Bármi, amit a netcat
kliensbe beírsz, el lesz küldve a szervernek, és visszhangként visszakapod.
Több Kliens Egyidejű Kezelése
A fenti alap echo szerver egyszerre csak egy klienst tud kezelni. Ha egy második kliens csatlakozik, miközben az első klienst még kiszolgálják, a második kliensnek várnia kell, amíg az első kliens le nem csatlakozik. Ez a legtöbb valós alkalmazás számára nem ideális. Több kliens egyidejű kezeléséhez használhatunk szálkezelést vagy elágaztatást.Szálkezelés
A szálkezelés lehetővé teszi több kliens egyidejű kezelését ugyanazon a folyamaton belül. Minden klienskapcsolat külön szálban kerül kezelésre, lehetővé téve a szerver számára, hogy továbbra is figyeljen az új kapcsolatokra, miközben más kliensek kiszolgálás alatt állnak. ASocketServer
modul biztosítja a ThreadingMixIn
osztályt, amely összekeverhető a szerver osztállyal a szálkezelés engedélyezéséhez.
Példa: Szálkezelésű Echo Szerver
import SocketServer
import threading
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024)
cur_thread = threading.current_thread()
response = "{}: {}".format(cur_thread.name, data)
self.request.sendall(response)
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print "Server loop running in thread:", server_thread.name
# ... (Your main thread logic here, e.g., simulating client connections)
# For example, to keep the main thread alive:
# while True:
# pass # Or perform other tasks
server.shutdown()
Magyarázat:
- Importáljuk a
threading
modult. - Létrehozunk egy
ThreadedTCPRequestHandler
osztályt, amely aSocketServer.BaseRequestHandler
-ből öröklődik. Ahandle()
metódus hasonló az előző példához, de tartalmazza az aktuális szál nevét is a válaszban. - Létrehozunk egy
ThreadedTCPServer
osztályt, amely aSocketServer.ThreadingMixIn
és aSocketServer.TCPServer
osztályokból is öröklődik. Ez a mix-in engedélyezi a szálkezelést a szerver számára. - Az
if __name__ == "__main__":
blokkban létrehozunk egyThreadedTCPServer
példányt, és elindítjuk egy külön szálban. Ez lehetővé teszi a fő szál számára a végrehajtás folytatását, miközben a szerver a háttérben fut.
Ez a szerver mostantól képes több klienskapcsolat egyidejű kezelésére. Minden kapcsolat külön szálban kerül kezelésre, lehetővé téve a szerver számára, hogy egyszerre több kliensre válaszoljon.
Elágaztatás
Az elágaztatás egy másik módja több kliens egyidejű kezelésének. Amikor egy új klienskapcsolat érkezik, a szerver egy új folyamatot ágaztat el a kapcsolat kezelésére. Minden folyamatnak saját memóriaterülete van, így a folyamatok el vannak szigetelve egymástól. A SocketServer
modul biztosítja a ForkingMixIn
osztályt, amely összekeverhető a szerver osztállyal az elágaztatás engedélyezéséhez. Megjegyzés: Az elágaztatást általában Unix-szerű rendszereken (Linux, macOS) használják, és előfordulhat, hogy nem érhető el vagy nem alkalmas Windows környezetekben.
Példa: Elágaztatott Echo Szerver
import SocketServer
import os
class ForkingTCPRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024)
pid = os.getpid()
response = "PID {}: {}".format(pid, data)
self.request.sendall(response)
class ForkingTCPServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = ForkingTCPServer((HOST, PORT), ForkingTCPRequestHandler)
ip, port = server.server_address
server.serve_forever()
Magyarázat:
- Importáljuk az
os
modult. - Létrehozunk egy
ForkingTCPRequestHandler
osztályt, amely aSocketServer.BaseRequestHandler
-ből öröklődik. Ahandle()
metódus tartalmazza a folyamatazonosítót (PID) a válaszban. - Létrehozunk egy
ForkingTCPServer
osztályt, amely aSocketServer.ForkingMixIn
és aSocketServer.TCPServer
osztályokból is öröklődik. Ez a mix-in engedélyezi az elágaztatást a szerver számára. - Az
if __name__ == "__main__":
blokkban létrehozunk egyForkingTCPServer
példányt, és elindítjuk aserver.serve_forever()
segítségével. Minden klienskapcsolat külön folyamatban kerül kezelésre.
Amikor egy kliens csatlakozik ehhez a szerverhez, a szerver egy új folyamatot ágaztat el a kapcsolat kezelésére. Minden folyamatnak saját PID-je lesz, lehetővé téve, hogy lássa, hogy a kapcsolatokat különböző folyamatok kezelik.
A Szálkezelés és az Elágaztatás Közötti Választás
A szálkezelés és az elágaztatás közötti választás számos tényezőtől függ, beleértve az operációs rendszert, az alkalmazás jellegét és a rendelkezésre álló erőforrásokat. Íme a legfontosabb szempontok összefoglalása:- Operációs Rendszer: Az elágaztatás általában előnyben részesített Unix-szerű rendszereken, míg a szálkezelés gyakoribb a Windows rendszereken.
- Erőforrás Felhasználás: Az elágaztatás több erőforrást fogyaszt, mint a szálkezelés, mivel minden folyamatnak saját memóriaterülete van. A szálkezelés megosztja a memóriaterületet, ami hatékonyabb lehet, de gondos szinkronizálást is igényel a versenyhelyzetek és más párhuzamossági problémák elkerülése érdekében.
- Bonyolultság: A szálkezelés bonyolultabb lehet megvalósítani és hibakeresni, mint az elágaztatás, különösen a megosztott erőforrások kezelésekor.
- Skálázhatóság: Az elágaztatás bizonyos esetekben jobban skálázható, mint a szálkezelés, mivel hatékonyabban kihasználhatja a több CPU magot. A folyamatok létrehozásának és kezelésének többletterhelése azonban korlátozhatja a skálázhatóságot.
Általánosságban elmondható, hogy ha egy egyszerű alkalmazást építesz Unix-szerű rendszeren, az elágaztatás jó választás lehet. Ha egy összetettebb alkalmazást építesz, vagy a Windows rendszert célzod meg, a szálkezelés megfelelőbb lehet. Fontos figyelembe venni a környezeted erőforrás korlátait és az alkalmazás potenciális skálázhatósági követelményeit is. A nagymértékben skálázható alkalmazások esetében fontolja meg az aszinkron frameworköket, mint például az `asyncio`, amelyek jobb teljesítményt és erőforráskihasználást kínálhatnak.
Egyszerű UDP Szerver Létrehozása
Az UDP (User Datagram Protocol) egy kapcsolat nélküli protokoll, amely gyorsabb, de kevésbé megbízható adatátvitelt biztosít, mint a TCP. Az UDP-t gyakran használják olyan alkalmazásokhoz, ahol a sebesség fontosabb, mint a megbízhatóság, például streaming média és online játékok. A SocketServer
modul biztosítja a UDPServer
osztályt az UDP szerverek létrehozásához.
Példa: UDP Echo Szerver
import SocketServer
class MyUDPHandler(SocketServer.BaseRequestHandler):
def handle(self):
data = self.request[0].strip()
socket = self.request[1]
print "{} wrote:".format(self.client_address[0])
print data
socket.sendto(data, self.client_address)
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = SocketServer.UDPServer((HOST, PORT), MyUDPHandler)
server.serve_forever()
Magyarázat:
- A
MyUDPHandler
osztályhandle()
metódusa fogadja az adatokat a klienstől. A TCP-vel ellentétben az UDP adatokat datagramként (adatcsomagként) fogadjuk. - A
self.request
attribútum egy tuple, amely tartalmazza az adatokat és a socketet. Az adatokat aself.request[0]
segítségével, a socketet pedig aself.request[1]
segítségével nyerjük ki. - A kapott adatokat visszaküldjük a kliensnek a
socket.sendto(data, self.client_address)
segítségével.
Ez a szerver fogadja az UDP datagramokat a kliensektől, és visszhangként visszaküldi azokat a feladónak.
Fejlett Technikák
Különböző Adatformátumok Kezelése
Sok valós alkalmazásban különböző adatformátumokat kell kezelnie, mint például a JSON, az XML vagy a Protocol Buffers. A Python beépített moduljait vagy harmadik féltől származó könyvtárakat használhat az adatok szerializálására és deszerializálására. Például a json
modul használható a JSON adatok kezelésére:
import SocketServer
import json
class JSONTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
try:
data = self.request.recv(1024).strip()
json_data = json.loads(data)
print "Received JSON data:", json_data
# Process the JSON data
response_data = {"status": "success", "message": "Data received"}
response_json = json.dumps(response_data)
self.request.sendall(response_json)
except ValueError as e:
print "Invalid JSON data received: {}".format(e)
self.request.sendall(json.dumps({"status": "error", "message": "Invalid JSON"}))
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = SocketServer.TCPServer((HOST, PORT), JSONTCPHandler)
server.serve_forever()
Ez a példa fogad JSON adatokat a klienstől, elemzi azokat a json.loads()
segítségével, feldolgozza azokat, és JSON választ küld vissza a kliensnek a json.dumps()
segítségével. A hibakezelés tartalmazza a hibás JSON adatok elfogását.
Hitelesítés Megvalósítása
A biztonságos alkalmazásokhoz hitelesítést kell végrehajtania a kliensek személyazonosságának ellenőrzéséhez. Ez különféle módszerekkel tehető meg, mint például felhasználónév/jelszó hitelesítés, API kulcsok vagy digitális tanúsítványok. Íme egy egyszerűsített példa a felhasználónév/jelszó hitelesítésre:
import SocketServer
import hashlib
# Replace with a secure way to store passwords (e.g., using bcrypt)
USER_CREDENTIALS = {
"user1": "password123",
"user2": "secure_password"
}
class AuthTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
# Authentication logic
username = self.request.recv(1024).strip()
password = self.request.recv(1024).strip()
if username in USER_CREDENTIALS and USER_CREDENTIALS[username] == password:
print "User {} authenticated successfully".format(username)
self.request.sendall("Authentication successful")
# Proceed with handling the client request
# (e.g., receive further data and process it)
else:
print "Authentication failed for user {}".format(username)
self.request.sendall("Authentication failed")
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = SocketServer.TCPServer((HOST, PORT), AuthTCPHandler)
server.serve_forever()
Fontos biztonsági megjegyzés: A fenti példa csak bemutatási célokat szolgál, és nem biztonságos. Soha ne tárolja a jelszavakat egyszerű szövegben. Használjon erős jelszó hashing algoritmust, mint például a bcrypt vagy az Argon2 a jelszavak tárolás előtti hash-eléséhez. Ezenkívül fontolja meg egy robusztusabb hitelesítési mechanizmus, például az OAuth 2.0 vagy a JWT (JSON Web Tokens) használatát éles környezetben.
Naplózás és Hibakezelés
A megfelelő naplózás és hibakezelés elengedhetetlen a szerver hibakereséséhez és karbantartásához. Használja a Pythonlogging
modulját az események, hibák és egyéb releváns információk rögzítéséhez. Implementáljon átfogó hibakezelést a kivételek kecses kezeléséhez és a szerver összeomlásának megakadályozásához. Mindig naplózzon elegendő információt a problémák hatékony diagnosztizálásához.
import SocketServer
import logging
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class LoggingTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
try:
data = self.request.recv(1024).strip()
logging.info("Received data from {}: {}".format(self.client_address[0], data))
self.request.sendall(data)
except Exception as e:
logging.exception("Error handling request from {}: {}".format(self.client_address[0], e))
self.request.sendall("Error processing request")
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = SocketServer.TCPServer((HOST, PORT), LoggingTCPHandler)
server.serve_forever()
Ez a példa konfigurálja a naplózást a bejövő kérésekkel kapcsolatos információk és a kérések kezelése során előforduló hibák rögzítésére. A logging.exception()
metódus a kivételek teljes veremkövetéssel történő naplózására szolgál, ami hasznos lehet a hibakereséshez.
A SocketServer Alternatívái
Míg aSocketServer
modul jó kiindulópont a socket programozás megismeréséhez, vannak bizonyos korlátai, különösen a nagy teljesítményű és skálázható alkalmazások esetében. Néhány népszerű alternatíva:
- asyncio: A Python beépített aszinkron I/O frameworkje. Az
asyncio
hatékonyabb módot kínál több egyidejű kapcsolat kezelésére coroutinek és eseményhurkok használatával. Általában előnyben részesítik a modern alkalmazások esetében, amelyek nagy párhuzamosságot igényelnek. - Twisted: Egy Pythonban írt eseményvezérelt hálózati motor. A Twisted gazdag funkciókészletet biztosít hálózati alkalmazások építéséhez, beleértve a különböző protokollok és párhuzamossági modellek támogatását.
- Tornado: Egy Python web framework és aszinkron hálózati könyvtár. A Tornadót nagyszámú egyidejű kapcsolat kezelésére tervezték, és gyakran használják valós idejű webalkalmazások építéséhez.
- ZeroMQ: Egy nagy teljesítményű aszinkron üzenetkezelő könyvtár. A ZeroMQ egyszerű és hatékony módot kínál elosztott rendszerek és üzenetsorok építéséhez.
Következtetés
A PythonSocketServer
modulja értékes bevezetést nyújt a hálózati programozásba, lehetővé téve az alapvető socket szerverek viszonylag egyszerű felépítését. A socketek, a TCP/UDP protokollok és a SocketServer
alkalmazások felépítésének alapvető koncepcióinak megértése elengedhetetlen a hálózati alapú alkalmazások fejlesztéséhez. Bár a SocketServer
nem feltétlenül alkalmas minden forgatókönyvre, különösen azokra, amelyek nagy skálázhatóságot vagy teljesítményt igényelnek, erős alapot teremt a fejlettebb hálózati technikák elsajátításához és az alternatív frameworkök, mint például az asyncio
, a Twisted és a Tornado felfedezéséhez. Az ebben az útmutatóban felvázolt elvek elsajátításával felkészült leszel a hálózati programozási kihívások széles körének kezelésére.
Nemzetközi Szempontok
Amikor socket szerver alkalmazásokat fejleszt egy globális közönség számára, fontos figyelembe venni a következő nemzetközivé tételi (i18n) és lokalizációs (l10n) tényezőket:
- Karakterkódolás: Győződj meg arról, hogy a szerver támogatja a különböző karakterkódolásokat, mint például az UTF-8, hogy a különböző nyelvekből származó szöveges adatokat helyesen tudja kezelni. Használj Unicode-ot belsőleg, és konvertáld a megfelelő kódolásra, amikor adatokat küldesz a klienseknek.
- Időzónák: Ügyelj az időzónákra az időbélyegek kezelésekor és az események ütemezésekor. Használj egy időzóna-tudatos könyvtárat, mint például a
pytz
a különböző időzónák közötti konvertáláshoz. - Szám- és Dátumformázás: Használj területi beállításokat figyelembe vevő formázást a számok és dátumok különböző régiók helyes formátumában történő megjelenítéséhez. A Python
locale
modulja használható erre a célra. - Nyelvi Fordítás: Fordítsd le a szerver üzeneteit és felhasználói felületét különböző nyelvekre, hogy az szélesebb közönség számára elérhető legyen.
- Pénznem Kezelése: Ha pénzügyi tranzakciókkal foglalkozol, győződj meg arról, hogy a szerver támogatja a különböző pénznemeket, és a helyes árfolyamokat használja.
- Jogi és Szabályozási Megfelelőség: Légy tisztában minden olyan jogi vagy szabályozási követelménnyel, amely a szervered működésére vonatkozhat a különböző országokban, mint például az adatvédelmi törvények (pl. GDPR).
Ezen nemzetközivé tételi szempontok figyelembevételével olyan socket szerver alkalmazásokat hozhatsz létre, amelyek elérhetők és felhasználóbarátak a globális közönség számára.